package com.tzh.plugin.ex;

import com.github.pagehelper.PageHelper;
import com.tzh.plugin.utils.GenerateUtils;
import com.tzh.plugin.utils.ServiceUtils;
import org.mybatis.generator.api.GeneratedJavaFile;
import org.mybatis.generator.api.GeneratedXmlFile;
import org.mybatis.generator.api.IntrospectedColumn;
import org.mybatis.generator.api.IntrospectedTable;
import org.mybatis.generator.api.PluginAdapter;
import org.mybatis.generator.api.dom.java.Field;
import org.mybatis.generator.api.dom.java.FullyQualifiedJavaType;
import org.mybatis.generator.api.dom.java.Interface;
import org.mybatis.generator.api.dom.java.JavaVisibility;
import org.mybatis.generator.api.dom.java.Method;
import org.mybatis.generator.api.dom.java.TopLevelClass;
import org.springframework.beans.BeanUtils;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Service;
import org.springframework.transaction.annotation.Transactional;

import java.util.ArrayList;
import java.util.HashMap;
import java.util.List;
import java.util.Map;

public class ImplServiceGeneratorPlugin extends PluginAdapter {

    @Override
    public boolean sqlMapGenerated(GeneratedXmlFile sqlMap, IntrospectedTable introspectedTable) {
        sqlMap.getFormattedContent();
        // TODO Auto-generated method stub
        return super.sqlMapGenerated(sqlMap, introspectedTable);
    }

    @Override
    public boolean clientGenerated(Interface interfaze,
                                   IntrospectedTable introspectedTable) {
        // TODO Auto-generated method stub
        return super.clientGenerated(interfaze, introspectedTable);
    }

    @Override
    public List<GeneratedJavaFile> contextGenerateAdditionalJavaFiles(IntrospectedTable introspectedTable) {
        String classType = introspectedTable.getBaseRecordType();
        String[] pathname = classType.split("\\.");
        String className = pathname[(pathname.length - 1)];
        String pkgPth = introspectedTable.getContext().getJavaModelGeneratorConfiguration().getTargetPackage();
        String[] path = pkgPth.split("\\.");
        StringBuilder serviceBuider = new StringBuilder();
        StringBuilder serviceImplBuider = new StringBuilder();
        StringBuilder dtoBuilder = new StringBuilder();
        int length = path.length;
        for (int i = 0; i < length - 1; i++) {
            serviceBuider.append(path[i]);
            if (i < (length - 2)) {
                serviceBuider.append(".");
            } else {
                dtoBuilder.append(serviceBuider);
                dtoBuilder.append(".dto");
                serviceBuider.append(".service");
                serviceImplBuider.append(serviceBuider);
                serviceImplBuider.append(".impl");
            }
        }

        List<GeneratedJavaFile> generatedJavaFiles = new ArrayList<GeneratedJavaFile>();
        GeneratedJavaFile serviceImplGxf = serviceImplGenerate(introspectedTable, className, classType, serviceImplBuider.toString(), dtoBuilder.toString());
        generatedJavaFiles.add(serviceImplGxf);
        return generatedJavaFiles;

    }

    private GeneratedJavaFile serviceImplGenerate(IntrospectedTable introspectedTable, String className, String classType, String servicePkg,
                                                  String dtoPkg) {
        FullyQualifiedJavaType interfaceType = new FullyQualifiedJavaType((servicePkg + ".I" + className + "Service").replace("impl.", ""));
        TopLevelClass cls = new TopLevelClass(servicePkg + "." + className + "ServiceImpl");
        cls.addSuperInterface(interfaceType);
        cls.addAnnotation("@Service");
        cls.setVisibility(JavaVisibility.PUBLIC);
        //引包
        cls.addImportedType(interfaceType);
        Map<String, FullyQualifiedJavaType> dtoData = new HashMap<String, FullyQualifiedJavaType>();
        GenerateUtils.setImports(cls, dtoPkg, className, classType, dtoData);
        cls.addImportedType(new FullyQualifiedJavaType(Autowired.class.getName()));
        cls.addImportedType(new FullyQualifiedJavaType(List.class.getName()));
        cls.addImportedType(new FullyQualifiedJavaType(Service.class.getName()));
        cls.addImportedType(new FullyQualifiedJavaType(Override.class.getName()));
        cls.addImportedType(new FullyQualifiedJavaType(ArrayList.class.getName()));
        cls.addImportedType(new FullyQualifiedJavaType(PageHelper.class.getName()));
        cls.addImportedType(new FullyQualifiedJavaType(classType));
        cls.addImportedType(new FullyQualifiedJavaType(classType + "Example"));
        cls.addImportedType(new FullyQualifiedJavaType(Transactional.class.getName()));

        FullyQualifiedJavaType mappperType = new FullyQualifiedJavaType(introspectedTable.getMyBatis3JavaMapperType());
        cls.addImportedType(mappperType);
        String mapperName = mappperType.getShortName();
        //工具类
        FullyQualifiedJavaType dtoTool1 = new FullyQualifiedJavaType(servicePkg.replace("service.impl", "utils.DtoUtils"));
//		FullyQualifiedJavaType dtoTool2 = new FullyQualifiedJavaType(servicePkg.replace("service.impl", "utils.BeanCopierUtils"));
        FullyQualifiedJavaType dtoTool3 = new FullyQualifiedJavaType(servicePkg.replace("service.impl", "utils.ServiceUtils"));
        FullyQualifiedJavaType dtoTool4 = new FullyQualifiedJavaType(BeanUtils.class.getName());
        cls.addImportedType(dtoTool1);
//		cls.addImportedType(dtoTool2);
        cls.addImportedType(dtoTool3);
        cls.addImportedType(dtoTool4);
        //autowired
        String mapper = mapperName.replace(String.valueOf(mapperName.charAt(0)), String.valueOf(mapperName.charAt(0)).toLowerCase());
        Field field = new Field(mapper, mappperType);
        field.setVisibility(JavaVisibility.PRIVATE);
        field.addAnnotation("@Autowired");
        cls.addField(field);
        //方法-查询
        createQueryMethod(className, dtoPkg, mapper, cls);
        //方法-新增
        createAddMethod(className, dtoPkg, mapper, cls);
        //方法-删除
        createDelMethod(className, dtoPkg, mapper, cls);
        //更新
        createUpdateMethod(className, dtoPkg, mapper, cls);

        List<IntrospectedColumn> primarys = introspectedTable.getPrimaryKeyColumns();
        //详情
        createDetailMethod(primarys, className, dtoPkg, mapper, cls);
        //删除
        createDelByIdMethod(primarys, className, dtoPkg, mapper, cls);
        GeneratedJavaFile gxf = null;
        try {
            gxf = new GeneratedJavaFile(cls, context.getJavaModelGeneratorConfiguration().getTargetProject(), context.getJavaFormatter());
        } catch (Exception e) {
            e.printStackTrace();
        }
        return gxf;
    }


    private void createQueryMethod(String className, String dtoPkg, String mapper, TopLevelClass cls) {
        Method queryMethod = ServiceUtils.generateMethod(className, dtoPkg, "query" + className + "List", 1);
        queryMethod.addAnnotation("@Override");
        queryMethod.addBodyLine("PageHelper.startPage(" + className.toLowerCase() + ".getPageNum(), " + className.toLowerCase() + ".getPageSize());");
        queryMethod.addBodyLine(className + "Example example = new " + className + "Example();");
        queryMethod.addBodyLine("DtoUtils.queryDto2Examle(" + className.toLowerCase() + ",example.createCriteria());");
        queryMethod.addBodyLine("Page<" + className + "> list = (Page<" + className + ">)" + mapper + ".selectByExample(example);");
        queryMethod.addBodyLine(queryMethod.getReturnType().get().getShortName() + " page =  ServiceUtils.getPageList(list, List" + className + "Dto.class);");
        queryMethod.addBodyLine("return page;");
        cls.addMethod(queryMethod);
    }

    private void createAddMethod(String className, String dtoPkg, String mapper, TopLevelClass cls) {
        Method addMethod = ServiceUtils.generateMethod(className, dtoPkg, "add" + className, 3);
        addMethod.addAnnotation("@Override");
        addMethod.addAnnotation("@Transactional");//Transactional
        addMethod.addBodyLine(className + " eo = new " + className + "();");
        addMethod.addBodyLine("BeanUtils.copyProperties(" + className.toLowerCase() + ", eo);");
        addMethod.addBodyLine("int m = " + mapper + ".insertSelective(eo);");
        addMethod.addBodyLine("if(m==0){");
        addMethod.addBodyLine("eo = null;");
        addMethod.addBodyLine("}");
        addMethod.addBodyLine("return eo;");
        cls.addMethod(addMethod);

    }


    private void createDelMethod(String className, String dtoPkg, String mapper, TopLevelClass cls) {
        Method delMethod = ServiceUtils.generateMethod(className, dtoPkg, "del" + className, 4);
        delMethod.addAnnotation("@Override");
        delMethod.addAnnotation("@Transactional");
        delMethod.addBodyLine(className + "Example example = new " + className + "Example();");
        delMethod.addBodyLine("DtoUtils.delDto2Examle(" + className.toLowerCase() + ",example.createCriteria());");
        delMethod.addBodyLine("int m = " + mapper + ".deleteByExample(example);");
        delMethod.addBodyLine("return m;");
        cls.addMethod(delMethod);

    }

    private void createDelByIdMethod(List<IntrospectedColumn> primarys, String className, String dtoPkg, String mapper, TopLevelClass cls) {
        Method delMethod = ServiceUtils.generateMethod(primarys, className, dtoPkg, "del" + className, 6);
        delMethod.addAnnotation("@Override");
        delMethod.addAnnotation("@Transactional");
        delMethod.addBodyLine(className + "Example example = new " + className + "Example();");
        delMethod.addBodyLine("example.createCriteria()");
        primarys.stream().forEach(col -> {
            String colName = col.getJavaProperty();
            String colStr = colName.toUpperCase().substring(0, 1) + colName.substring(1, colName.length());
            delMethod.addBodyLine(".and" + colStr + "EqualTo(" + colName + ")");
        });
        delMethod.addBodyLine(";");
        delMethod.addBodyLine("int m = " + mapper + ".deleteByExample(example);");
        delMethod.addBodyLine("return m;");
        cls.addMethod(delMethod);
    }

    private void createUpdateMethod(String className, String dtoPkg, String mapper, TopLevelClass cls) {
        Method utMethod = ServiceUtils.generateMethod(className, dtoPkg, "update" + className, 5);
        utMethod.addAnnotation("@Override");
        utMethod.addAnnotation("@Transactional");
        utMethod.addBodyLine(className + " eo = new " + className + "();");
        utMethod.addBodyLine("BeanUtils.copyProperties(" + className.toLowerCase() + ", eo);");
        utMethod.addBodyLine("int m = 0;");
        utMethod.addBodyLine("m = " + mapper + ".updateByPrimaryKeySelective(eo);");
        utMethod.addBodyLine("return m;");
        cls.addMethod(utMethod);
    }

    private void createDetailMethod(List<IntrospectedColumn> primarys, String className, String dtoPkg, String mapper, TopLevelClass cls) {
        Method detailMethod = ServiceUtils.generateMethod(primarys, className, dtoPkg, "select" + className + "Detail", 7);
        String dtoName = detailMethod.getReturnType().get().getShortName();
        detailMethod.addAnnotation("@Override");
        detailMethod.addBodyLine(className + "Example example = new " + className + "Example();");
        detailMethod.addBodyLine("example.createCriteria()");
        primarys.stream().forEach(col -> {
            String colName = col.getJavaProperty();
            String colStr = colName.toUpperCase().substring(0, 1) + colName.substring(1, colName.length());
            detailMethod.addBodyLine(".and" + colStr + "EqualTo(" + colName + ")");
        });
        detailMethod.addBodyLine(";");
        detailMethod.addBodyLine("List<" + className + "> list = " + mapper + ".selectByExample(example);");
        detailMethod.addBodyLine(className + " eo = null;");
        detailMethod.addBodyLine(dtoName + " dto = new " + dtoName + "();");
        detailMethod.addBodyLine("if(list!=null&&list.size()>0) {");
        detailMethod.addBodyLine("eo = list.get(0);");
        detailMethod.addBodyLine("BeanUtils.copyProperties(eo, dto);");
        detailMethod.addBodyLine("}");
        detailMethod.addBodyLine("return dto;");
        cls.addMethod(detailMethod);

    }

    @Override
    public boolean validate(List<String> warnings) {
        return true;
    }
}
